﻿
using System.Text;

namespace NetworkTools {
    public class SubnetCalculator {
        public static readonly string[] SUBNETMASK_CLASS_A = new[] { "255.0.0.0", "255.128.0.0", "255.192.0.0", "255.224.0.0", "255.240.0.0", "255.248.0.0", "255.252.0.0", "255.254.0.0", "255.255.0.0", "255.255.128.0", "255.255.192.0", "255.255.224.0", "255.255.240.0", "255.255.248.0", "255.255.252.0", "255.255.254.0", "255.255.255.0", "255.255.255.128", "255.255.255.192", "255.255.255.224", "255.255.255.240", "255.255.255.248", "255.255.255.252" };
        public static readonly string[] SUBNETMASK_CLASS_B = new[] { "255.255.0.0", "255.255.128.0", "255.255.192.0", "255.255.224.0", "255.255.240.0", "255.255.248.0", "255.255.252.0", "255.255.254.0", "255.255.255.0", "255.255.255.128", "255.255.255.192", "255.255.255.224", "255.255.255.240", "255.255.255.248", "255.255.255.252" };
        public static readonly string[] SUBNETMASK_CLASS_C = new[] { "255.255.255.0", "255.255.255.128", "255.255.255.192", "255.255.255.224", "255.255.255.240", "255.255.255.248", "255.255.255.252" };

        public static readonly int[] SUBNETBITS_CLASS_A = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 };
        public static readonly int[] SUBNETBITS_CLASS_B = new[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 };
        public static readonly int[] SUBNETBITS_CLASS_C = new[] { 0, 1, 2, 3, 4, 5, 6 };

        public static readonly int[] MAXSUBNETS_CLASS_A = new[] { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304 };
        public static readonly int[] MAXSUBNETS_CLASS_B = new[] { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384 };
        public static readonly int[] MAXSUBNETS_CLASS_C = new[] { 1, 2, 4, 8, 16, 32, 64 };

        public static readonly int[] MASKBITS_CLASS_A = new[] { 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 };
        public static readonly int[] MASKBITS_CLASS_B = new[] { 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 };
        public static readonly int[] MASKBITS_CLASS_C = new[] { 24, 25, 26, 27, 28, 29, 30 };

        public static readonly int[] HOSTSPERSUBNET_CLASS_A = new[] { 16777214, 8388606, 4194302, 2097150, 1048574, 524286, 262142, 131070, 65534, 32766, 16382, 8190, 4094, 2046, 1022, 510, 254, 126, 62, 30, 14, 6, 2 };
        public static readonly int[] HOSTSPERSUBNET_CLASS_B = new[] { 65534, 32766, 16382, 8190, 4094, 2046, 1022, 510, 254, 126, 62, 30, 14, 6, 2 };
        public static readonly int[] HOSTSPERSUBNET_CLASS_C = new[] { 254, 126, 62, 30, 14, 6, 2 };

        public static int GetSubnetBitsFromMaskBits(NetworkClass networkClass, int maskBits) {
            int idx;
            switch (networkClass) {
                case NetworkClass.A:
                    idx = MASKBITS_CLASS_A.IndexOf(maskBits).Value;
                    return SUBNETBITS_CLASS_A[idx];
                case NetworkClass.B:
                    idx = MASKBITS_CLASS_B.IndexOf(maskBits).Value;
                    return SUBNETBITS_CLASS_B[idx];
                case NetworkClass.C:
                    idx = MASKBITS_CLASS_C.IndexOf(maskBits).Value;
                    return SUBNETBITS_CLASS_C[idx];
            }
            return 0;
        }

        public static int GetSubnetBitsFromMaximumSubnets(NetworkClass networkClass, int maximumSubnets) {
            int idx;
            switch (networkClass) {
                case NetworkClass.A:
                    idx = MAXSUBNETS_CLASS_A.IndexOf(maximumSubnets).Value;
                    return SUBNETBITS_CLASS_A[idx];
                case NetworkClass.B:
                    idx = MAXSUBNETS_CLASS_B.IndexOf(maximumSubnets).Value;
                    return SUBNETBITS_CLASS_B[idx];
                case NetworkClass.C:
                    idx = MAXSUBNETS_CLASS_C.IndexOf(maximumSubnets).Value;
                    return SUBNETBITS_CLASS_C[idx];
            }
            return 0;
        }

        public static int GetSubnetBitsFromHostsPerSubnet(NetworkClass networkClass, int hostsPerSubnet) {
            int idx;
            switch (networkClass) {
                case NetworkClass.A:
                    idx = HOSTSPERSUBNET_CLASS_A.IndexOf(hostsPerSubnet).Value;
                    return SUBNETBITS_CLASS_A[idx];
                case NetworkClass.B:
                    idx = HOSTSPERSUBNET_CLASS_B.IndexOf(hostsPerSubnet).Value;
                    return SUBNETBITS_CLASS_B[idx];
                case NetworkClass.C:
                    idx = HOSTSPERSUBNET_CLASS_C.IndexOf(hostsPerSubnet).Value;
                    return SUBNETBITS_CLASS_C[idx];
            }
            return 0;
        }

        public static int GetSubnetBitsFromSubnetMask(NetworkClass networkClass, string subnetMask) {
            int idx;
            switch (networkClass) {
                case NetworkClass.A:
                    idx = SUBNETMASK_CLASS_A.IndexOf(subnetMask).Value;
                    return SUBNETBITS_CLASS_A[idx];
                case NetworkClass.B:
                    idx = SUBNETMASK_CLASS_B.IndexOf(subnetMask).Value;
                    return SUBNETBITS_CLASS_B[idx];
                case NetworkClass.C:
                    idx = SUBNETMASK_CLASS_C.IndexOf(subnetMask).Value;
                    return SUBNETBITS_CLASS_C[idx];
            }
            return 0;
        }

        public static string GetFirstOctetRange(NetworkClass networkClass) {
            switch (networkClass) {
                case NetworkClass.A:
                    return "1 - 126";
                case NetworkClass.B:
                    return "128 - 191";
                case NetworkClass.C:
                    return "192 - 223";
                default:
                    return null;
            }
        }

        public static string GetHexIpAddress(byte[] addressBytes) {
            return string.Format("{0:X2}.{1:X2}.{2:X2}.{3:X2}",
                addressBytes[0],
                addressBytes[1],
                addressBytes[2],
                addressBytes[3]);
        }

        public static string GetSubnetId(NetworkClass networkClass, byte[] addressBytes) {
            switch (networkClass) {
                case NetworkClass.C:
                    return string.Format("{0}.{1}.{2}.0",
                        addressBytes[0],
                        addressBytes[1],
                        addressBytes[2]);
                case NetworkClass.B:
                    return string.Format("{0}.{1}.0.0",
                        addressBytes[0],
                        addressBytes[1]);
                case NetworkClass.A:
                    return string.Format("{0}.0.0.0",
                        addressBytes[0]);
                default:
                    return null;
            }
        }

        public static string GetSubnetBitmap(NetworkClass networkClass, int subnetBits) {
            StringBuilder result = new StringBuilder();
            int maxSubnetBits = 0;
            int idx = 0;
            switch (networkClass) {
                case NetworkClass.C:
                    result.Append("110nnnnn.nnnnnnnn.nnnnnnnn");
                    maxSubnetBits = 6;
                    idx = 24;
                    break;
                case NetworkClass.B:
                    result.Append("10nnnnnn.nnnnnnnn");
                    maxSubnetBits = 14;
                    idx = 16;
                    break;
                case NetworkClass.A:
                    result.Append("0nnnnnnn");
                    maxSubnetBits = 22;
                    idx = 8;
                    break;
            }
            for (int i = 0; i < subnetBits; i++) {
                if (idx == 8 || idx == 16 || idx == 24) {
                    result.Append(".");
                }
                result.Append("s");
                idx++;
            }
            for (int i = 0; i < maxSubnetBits - subnetBits; i++) {
                if (idx == 8 || idx == 16 || idx == 24) {
                    result.Append(".");
                }
                result.Append("h");
                idx++;
            }

            return result.ToString();
        }

        public static int GetMaskBitsFromSubnetBits(NetworkClass networkClass, int subnetBits) {
            int idx;
            switch (networkClass) {
                case NetworkClass.A:
                    idx = SUBNETBITS_CLASS_A.IndexOf(subnetBits).Value;
                    return MASKBITS_CLASS_A[idx];
                case NetworkClass.B:
                    idx = SUBNETBITS_CLASS_B.IndexOf(subnetBits).Value;
                    return MASKBITS_CLASS_B[idx];
                case NetworkClass.C:
                    idx = SUBNETBITS_CLASS_C.IndexOf(subnetBits).Value;
                    return MASKBITS_CLASS_C[idx];
            }
            return 0;
        }

        public static int GetMaximumSubnetsFromSubnetBits(NetworkClass networkClass, int subnetBits) {
            int idx;
            switch (networkClass) {
                case NetworkClass.A:
                    idx = SUBNETBITS_CLASS_A.IndexOf(subnetBits).Value;
                    return MAXSUBNETS_CLASS_A[idx];
                case NetworkClass.B:
                    idx = SUBNETBITS_CLASS_B.IndexOf(subnetBits).Value;
                    return MAXSUBNETS_CLASS_B[idx];
                case NetworkClass.C:
                    idx = SUBNETBITS_CLASS_C.IndexOf(subnetBits).Value;
                    return MAXSUBNETS_CLASS_C[idx];
            }
            return 0;
        }

        public static int GetHostsPerSubnetFromSubnetBits(NetworkClass networkClass, int subnetBits) {
            int idx;
            switch (networkClass) {
                case NetworkClass.A:
                    idx = SUBNETBITS_CLASS_A.IndexOf(subnetBits).Value;
                    return HOSTSPERSUBNET_CLASS_A[idx];
                case NetworkClass.B:
                    idx = SUBNETBITS_CLASS_B.IndexOf(subnetBits).Value;
                    return HOSTSPERSUBNET_CLASS_B[idx];
                case NetworkClass.C:
                    idx = SUBNETBITS_CLASS_C.IndexOf(subnetBits).Value;
                    return HOSTSPERSUBNET_CLASS_C[idx];
            }
            return 0;
        }

        public static string GetWildcardMaskFromSubnetBits(NetworkClass networkClass, int subnetBits) {
            return GetIpAddressAsString(~GetSubnetMaskAsUint(networkClass, subnetBits));
        }

        public static string GetHostAddressRangeFromSubnetBits(NetworkClass networkClass, byte[] ipAddress, int subnetBits) {
            uint ipAddressUint = GetIpAddressAsUint(ipAddress);
            return string.Format("{0} - {1}",
                GetIpAddressAsString(GetHostAddressRangeFromSubnetBitsMin(networkClass, ipAddressUint, subnetBits)),
                GetIpAddressAsString(GetHostAddressRangeFromSubnetBitsMax(networkClass, ipAddressUint, subnetBits)));
        }

        private static uint GetIpAddressAsUint(byte[] address) {
            uint result = ((uint)address[0]) << 24;
            result |= ((uint)address[1]) << 16;
            result |= ((uint)address[2]) << 8;
            result |= ((uint)address[3]) << 0;
            return result;
        }

        private static uint GetHostAddressRangeFromSubnetBitsMin(NetworkClass networkClass, uint ipAddress, int subnetBits) {
            return (ipAddress & GetSubnetMaskAsUint(networkClass, subnetBits)) | 1;
        }

        private static uint GetHostAddressRangeFromSubnetBitsMax(NetworkClass networkClass, uint ipAddress, int subnetBits) {
            uint subnetMask = GetSubnetMaskAsUint(networkClass, subnetBits);
            return (ipAddress & subnetMask) | (0xfffffffe & ~subnetMask);
        }

        public static string GetBroadcastAddressFromSubnetBits(int subnetBits) {
            return null;
        }

        public static string GetIpAddressAsString(uint ipAddress) {
            byte[] octets = new byte[4];
            octets[0] = (byte)((ipAddress >> 24) & 0xff);
            octets[1] = (byte)((ipAddress >> 16) & 0xff);
            octets[2] = (byte)((ipAddress >> 8) & 0xff);
            octets[3] = (byte)((ipAddress >> 0) & 0xff);
            return GetIpAddressAsString(octets);
        }

        private static string GetIpAddressAsString(byte[] octets) {
            return string.Format("{0}.{1}.{2}.{3}",
                                 octets[0],
                                 octets[1],
                                 octets[2],
                                 octets[3]);
        }

        public static uint GetSubnetMaskAsUint(NetworkClass networkClass, int subnetBits) {
            uint results;
            switch (networkClass) {
                case NetworkClass.A:
                    results = 0xffffffff << (24 - subnetBits);
                    break;
                case NetworkClass.B:
                    results = 0xffffffff << (16 - subnetBits);
                    break;
                case NetworkClass.C:
                    results = 0xffffffff << (8 - subnetBits);
                    break;
                default:
                    results = 0;
                    break;
            }
            return results;
        }
    }
}
